package com.ruoyi.system.controller;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;

import com.ruoyi.common.constant.UserConstants;
import com.ruoyi.common.core.domain.model.LoginUser;
import com.ruoyi.common.core.utils.SecurityUtils;
import com.ruoyi.common.core.utils.StringUtils;
import com.ruoyi.common.log.annotation.Log;
import com.ruoyi.common.log.enums.BusinessType;
import com.ruoyi.security.annotation.PreAuthorize;
import com.ruoyi.security.service.TokensService;
import com.ruoyi.system.domain.SysMenu;
import com.ruoyi.system.domain.SysUserMenu;
import com.ruoyi.system.domain.vo.GetAllMenusVo;
import com.ruoyi.system.domain.vo.MenuAddVo;
import com.ruoyi.system.domain.vo.MenuEditVo;
import com.ruoyi.system.service.ISysMenuService;
import com.ruoyi.system.service.ISysUserMenuService;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;

import java.util.*;
import java.util.stream.Collectors;

/**
 * 菜单信息
 *
 * @author ruoyi
 */
@RestController
@RequestMapping("/system/menu")
public class SysMenuController extends com.ruoyi.common.core.web.controller.BaseController {
    @Autowired
    private ISysMenuService menuService;
    @Autowired
    private ISysUserMenuService userMenuService;
    @Autowired
    private TokensService tokenService;

    /**
     * 获取菜单列表
     */
    @PreAuthorize
    //@PreAuthorize(hasPermi = "system:menu:list")
    @GetMapping("/list")
    @ApiOperation("获取菜单列表(支持筛选)\n" +
            "(name-菜单名称-String-非必填)\n" +
            "(menuType-菜单类型(M目录 C菜单 F按钮)-String-非必填)\n" +
            "(status-状态-String-非必填)")
    public com.ruoyi.common.core.web.page.TableDataInfo list(SysMenu menu) {
        Long userId = SecurityUtils.getUserId();
        startPage();
        List<SysMenu> menus = menuService.selectMenuList(menu, userId);
        List<SysMenu> menuTree = menuService.buildMenuTree(menus);
        return getDataTable(menuTree);
    }

    /**
     * 根据菜单编号获取详细信息
     */
    @PreAuthorize
    //@PreAuthorize(hasPermi = "system:menu:query")
    @GetMapping(value = "/{menuId}")
    @ApiOperation("根据菜单ID获取详细信息(修改菜单信息时会用到，菜单ID放到请求路径中)\n" +
            "(menuId-菜单ID-Long-必填)")
    public com.ruoyi.common.core.web.domain.AjaxResult getInfo(@PathVariable Long menuId) {
        return com.ruoyi.common.core.web.domain.AjaxResult.success(menuService.selectMenuById(menuId));
    }

    /**
     * 获取菜单下拉树列表
     */
    @GetMapping("/treeselect")
    @ApiOperation("获取菜单下拉树列表\n" +
            "(menuName-菜单名称-String-非必填)\n" +
            "(status-状态-char-非必填)")
    public com.ruoyi.common.core.web.domain.AjaxResult treeselect(SysMenu menu) {
        Long userId = SecurityUtils.getUserId();
        List<SysMenu> menus = menuService.selectMenuList(menu, 1L);
        return com.ruoyi.common.core.web.domain.AjaxResult.success(menuService.buildMenuTreeSelect(menus));
    }

    /**
     * 加载对应角色菜单列表树
     */
    @GetMapping(value = "/roleMenuTreeselect/{roleId}")
    @ApiOperation("加载对应角色菜单列表树\n" +
            "(roleId-用户角色ID-Long-必填")
    public com.ruoyi.common.core.web.domain.AjaxResult roleMenuTreeselect(@PathVariable("roleId") Long roleId) {
        Long userId = SecurityUtils.getUserId();
        List<SysMenu> menus = menuService.selectMenuList(userId);
        com.ruoyi.common.core.web.domain.AjaxResult ajax = com.ruoyi.common.core.web.domain.AjaxResult.success();
        ajax.put("checkedKeys", menuService.selectMenuListByRoleId(roleId));
        ajax.put("menus", menuService.buildMenuTreeSelect(menus));
        return ajax;
    }

    /**
     * 新增同级菜单
     */
    @PreAuthorize
    //@PreAuthorize(hasPermi = "system:menu:add")
    @Log(title = "菜单管理", businessType = BusinessType.INSERT)
    @PostMapping("/addSibling")
    @ApiOperation("新增同级菜单")
    public com.ruoyi.common.core.web.domain.AjaxResult addSibling(@Validated @RequestBody MenuAddVo vo) {
        SysMenu menu = new SysMenu();
        BeanUtils.copyProperties(vo, menu);
        if (UserConstants.NOT_UNIQUE.equals(menuService.checkNameUnique(menu))) {
            return com.ruoyi.common.core.web.domain.AjaxResult.error("新增菜单'" + menu.getName() + "'失败，菜单名称已存在");
        } else if (UserConstants.YES_FRAME.equals(menu.getIsFrame()) && !StringUtils.ishttp(menu.getPath())) {
            return com.ruoyi.common.core.web.domain.AjaxResult.error("新增菜单'" + menu.getName() + "'失败，地址必须以http(s)://开头");
        }
        SysMenu sysMenu = menuService.selectMenuById(vo.getMenuId());
        if (sysMenu != null){
            menu.setParentId(sysMenu.getParentId());
        } else {
            menu.setParentId(0L);
        }
        menu.setCreateBy(SecurityUtils.getUsername());
        return toAjax(menuService.insertMenu(menu));
    }

    /**
     * 新增下级菜单
     */
    @PreAuthorize
    //@PreAuthorize(hasPermi = "system:menu:add")
    @Log(title = "菜单管理", businessType = BusinessType.INSERT)
    @PostMapping("/addLower")
    @ApiOperation("新增下级菜单")
    public com.ruoyi.common.core.web.domain.AjaxResult addLower(@Validated @RequestBody MenuAddVo vo) {
        SysMenu menu = new SysMenu();
        BeanUtils.copyProperties(vo, menu);
        if (UserConstants.NOT_UNIQUE.equals(menuService.checkNameUnique(menu))) {
            return com.ruoyi.common.core.web.domain.AjaxResult.error("新增菜单'" + menu.getName() + "'失败，菜单名称已存在");
        } else if (UserConstants.YES_FRAME.equals(menu.getIsFrame()) && !StringUtils.ishttp(menu.getPath())) {
            return com.ruoyi.common.core.web.domain.AjaxResult.error("新增菜单'" + menu.getName() + "'失败，地址必须以http(s)://开头");
        }
        menu.setCreateBy(SecurityUtils.getUsername());
        return toAjax(menuService.insertMenu(menu));
    }

    /**
     * 修改菜单
     */
    @PreAuthorize
    //@PreAuthorize(hasPermi = "system:menu:edit")
    @Log(title = "菜单管理", businessType = BusinessType.UPDATE)
    @PutMapping
    @ApiOperation("修改菜单")
    public com.ruoyi.common.core.web.domain.AjaxResult edit(@Validated @RequestBody MenuEditVo vo) {
        SysMenu menu = new SysMenu();
        BeanUtils.copyProperties(vo, menu);
        if (UserConstants.NOT_UNIQUE.equals(menuService.checkNameUnique(menu))) {
            return com.ruoyi.common.core.web.domain.AjaxResult.error("修改菜单'" + menu.getName() + "'失败，菜单名称已存在");
        } else if (UserConstants.YES_FRAME.equals(menu.getIsFrame()) && !StringUtils.ishttp(menu.getPath())) {
            return com.ruoyi.common.core.web.domain.AjaxResult.error("修改菜单'" + menu.getName() + "'失败，地址必须以http(s)://开头");
        } else if (menu.getMenuId().equals(menu.getParentId())) {
            return com.ruoyi.common.core.web.domain.AjaxResult.error("修改菜单'" + menu.getName() + "'失败，上级菜单不能选择自己");
        }
        menu.setUpdateBy(SecurityUtils.getUsername());
        return toAjax(menuService.updateMenu(menu));
    }

    /**
     * 删除菜单
     */
    @PreAuthorize
    //@PreAuthorize(hasPermi = "system:menu:remove")
    @Log(title = "菜单管理", businessType = BusinessType.DELETE)
    @DeleteMapping("/{menuId}")
    @ApiOperation("删除菜单\n" +
            "(menuId-菜单ID-Long-必填)")
    public com.ruoyi.common.core.web.domain.AjaxResult remove(@PathVariable("menuId") Long menuId) {
        if (menuService.hasChildByMenuId(menuId)) {
            return com.ruoyi.common.core.web.domain.AjaxResult.error("存在子菜单,不允许删除");
        }
        if (menuService.checkMenuExistRole(menuId)) {
            return com.ruoyi.common.core.web.domain.AjaxResult.error("菜单已分配,不允许删除");
        }
        return toAjax(menuService.deleteMenuById(menuId));
    }

    /**
     * 获取路由信息
     *
     * @return 路由信息
     */
    @GetMapping("getRouters")
    @ApiOperation("获取路由信息")
    public com.ruoyi.common.core.web.domain.AjaxResult getRouters() {
        Long userId = SecurityUtils.getUserId();
        List<SysMenu> menus = menuService.selectMenuTreeByUserId(userId);
        return com.ruoyi.common.core.web.domain.AjaxResult.success(menuService.buildMenus(menus));
    }

    public List<GetAllMenusVo> getUserCanLookMenus() {
        LoginUser loginUser = tokenService.getLoginUser();
        Set<String> roles = loginUser.getRoles();
        List<SysMenu> menus = menuService.list(new QueryWrapper<SysMenu>().eq(SysMenu.IS_APP, 0));
        Set<Long> distinctSet = new HashSet<>(); //用于将菜单去重
        roles.forEach(r -> {
            Set<Long> menuIds = menus.stream().filter(m -> m.getPerms().contains(r)).map(SysMenu::getMenuId)
                    .collect(Collectors.toSet());
            distinctSet.addAll(menuIds);
        });
        List<SysMenu> sysMenus = menus.stream().filter(m -> distinctSet.contains(m.getMenuId())).collect(Collectors.toList());
        List<GetAllMenusVo> resultMenu = new ArrayList<>();
        sysMenus.forEach(m -> {
            GetAllMenusVo vo = new GetAllMenusVo();
            BeanUtils.copyProperties(m, vo);
            resultMenu.add(vo);
        });
        return resultMenu;
    }

    @ApiOperation("获取我的菜单")
    @GetMapping("/getMyMenus")
    public com.ruoyi.common.core.web.domain.AjaxResult getMyMenus() {
        List<SysUserMenu> userMenus = userMenuService.list(new QueryWrapper<SysUserMenu>()
                .eq(SysUserMenu.USER_ID, SecurityUtils.getUserId()));
        Set<Long> menuIds = userMenus.stream().map(SysUserMenu::getMenuId).collect(Collectors.toSet());
        List<SysMenu> menuList = new ArrayList<>();
        if (menuIds.size() > 0) {
            menuList = menuService.list(new QueryWrapper<SysMenu>().in(SysMenu.MENU_ID, menuIds));
        }
        List<GetAllMenusVo> resultMenu = new ArrayList<>();
        if (menuList.size() == 0) {
            //若用户没有自定义菜单，则使用默认的，默认展示前4个菜单
            List<GetAllMenusVo> collect = getUserCanLookMenus().stream().limit(4).collect(Collectors.toList());
            resultMenu.addAll(collect);
        } else {
            //若用户自定义了菜单，则使用
            menuList.forEach(m -> {
                GetAllMenusVo vo = new GetAllMenusVo();
                BeanUtils.copyProperties(m, vo);
                resultMenu.add(vo);
            });
        }
        resultMenu.sort(Comparator.comparing(GetAllMenusVo::getOrderNum));
        return com.ruoyi.common.core.web.domain.AjaxResult.success(resultMenu);
    }

    @ApiOperation("根据当前用户的角色查看所有小程序菜单")
    @GetMapping("/getAllMenus")
    public com.ruoyi.common.core.web.domain.AjaxResult getAllMenus() {
        return com.ruoyi.common.core.web.domain.AjaxResult.success(getUserCanLookMenus());
    }

    @ApiOperation("修改我的菜单")
    @PostMapping("/updateMyMenus")
    public com.ruoyi.common.core.web.domain.AjaxResult updateMyMenus(@ApiParam("修改后的菜单id的数组,id的顺序代表菜单摆放的顺序") @RequestBody List<Long> menuIds) {
        List<SysUserMenu> userMenus = userMenuService.list(new QueryWrapper<SysUserMenu>()
                .eq(SysUserMenu.USER_ID, SecurityUtils.getUserId()).orderByAsc(SysUserMenu.ORDER_NUM));
        //现在的菜单
        Long[] arr1 = userMenus.stream().map(SysUserMenu::getMenuId).toArray(Long[]::new);
        //修改后的菜单
        Long[] arr2 = menuIds.toArray(new Long[0]);
        //若两个菜单数组的内容或顺序不相等，则修改
        if (!Arrays.equals(arr1, arr2)) {
            userMenuService.remove(new QueryWrapper<SysUserMenu>().eq(SysUserMenu.USER_ID, SecurityUtils.getUserId()));
            List<SysUserMenu> result = new ArrayList<>();
            for (int i = 0; i < arr2.length; i++) {
                SysUserMenu userMenu = new SysUserMenu();
                userMenu.setUserId(SecurityUtils.getUserId())
                        .setMenuId(arr2[i])
                        .setOrderNum(i + 1);
                result.add(userMenu);
            }
            userMenuService.saveBatch(result);
        }
        return com.ruoyi.common.core.web.domain.AjaxResult.success();
    }
}